<?php

namespace App\Model;

use App\Model\Admin\AdminModel;
use App\Traits\LogTrait;
use EasySwoole\Component\Context\ContextManager;
use EasySwoole\EasySwoole\Config;
use EasySwoole\Http\Request;
use EasySwoole\Mysqli\QueryBuilder;
use EasySwoole\ORM\AbstractModel;
use EasySwoole\ORM\DbManager;
use Swoole\Exception;

/**
 * 模型基类
 */
abstract class BaseModel extends AbstractModel
{
    use LogTrait;

    // 自动写入时间戳字段，true开启false关闭
    protected $autoTimeStamp = false;
    // 创建时间字段自定义，默认create_time
    protected $createTime = 'create_time';
    // 更新时间字段自定义，默认update_time
    protected $updateTime = 'update_time';

    protected $gender_list = [
                1 => '男',
                2 => '女',
                3 => '未知',
            ];


    /**
     * 查询多条记录
     * @param array $where 查询条件
     * @param array $sort 排序
     * @param string $limit 限制条数
     * @param array $append 追加字段
     * @param array $visible 显示字段
     * @param array $hidden 隐藏字段
     * @return array
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \Throwable
     */
    public function getList(array $where = [], $sort = [], string $limit = '', $append = [], $visible=[], $hidden = [])
    {
        // 查询条件
        // 可以是一个一维索引数组，如['id', '1', '=', 'AND']
        // 可以是一个一维索引数组，如[1] // 表示查询主键为1的记录；
        // 可以是一个一维索引数组，如[function(QueryBuilder $builder){}] // 传入回调函数来操作构建器
        // 可以是一个二维索引数组，如[[1,2,3,4]] // 表示查询主键为1,2,3,4的记录；
        // 可以是一个二维关联数组，如[['id' => [1,'=', 'AND'], 'name' => ['test', 'like', 'OR']]]
        foreach ($where as $condition) {
            $this->where($condition);
        }

        // 排序，如['id' => 'desc', 'sort' => 'asc']
        if (!empty($sort) && is_array($sort)) {
            foreach ($sort as $key => $value) {
                $this->order($key, $value);
            }
        }

        // 分页设置，如'5,10'
        if (!empty($limit)) {
            list($offset, $page_size) = explode(',', $limit);
            $this->limit((int)$offset, (int)$page_size);
        }

        $collection = $this->all();

        if (empty($collection)) {
            throw new \EasySwoole\ORM\Exception\Exception('查询为空', EXCEPTION_CODE_QUERY);
        }

        // 默认追加字段
        $collection->append(['create_name', 'update_name']); // 获取记录创建者和更新者

        // 追加字段
        if (!empty($append)) {
            $collection->append($append);
        }

        // 显示字段
        if (!empty($visible)) {
            $collection->visible($visible);
        }

        // 隐藏字段
        if (!empty($hidden)) {
            $collection->hidden($hidden);
        }

        return $collection->toArray(false, true);
    }

    /**
     * 过滤数据列表
     * @param array $append
     * @param array $visible
     * @param array $hidden
     * @return array
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \Throwable
     */
    public function getListWithDataFilter(array $append = [], array $visible=[], array $hidden = [])
    {
        return $this->getList([], [], '', $append, $visible, $hidden);
    }

    /**
     * 查询一条记录
     * @param array $where 查询条件
     * @param array $append 追加字段
     * @param array $visible 显示字段
     * @param array $hidden 隐藏字段
     * @return array
     * @throws \EasySwoole\Mysqli\Exception\Exception
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \Throwable
     */
    public function getOne(array $where = [], $append = [], $visible=[], $hidden = [])
    {
        // 查询条件
        // 可以是一个一维索引数组，如['id', '1', '=', 'AND']
        // 可以是一个一维索引数组，如[1] // 表示查询主键为1的记录；
        // 可以是一个一维索引数组，如[function(QueryBuilder $builder){}] // 传入回调函数来操作构建器
        // 可以是一个二维索引数组，如[[1,2,3,4]] // 表示查询主键为1,2,3,4的记录；
        // 可以是一个二维关联数组，如[['id' => [1,'=', 'AND'], 'name' => ['test', 'like', 'OR']]]
        foreach ($where as $condition) {
            $this->where($condition);
        }

        $collection = $this->get();

        if (empty($collection)) {
            throw new \EasySwoole\ORM\Exception\Exception('查询为空', EXCEPTION_CODE_QUERY);
        }

        // 默认追加字段
        $collection->append(['create_name', 'update_name']); // 获取记录创建者和更新者

        // 追加字段
        if (empty($append)) {
            $collection->append($append);
        }

        // 显示字段
        if (empty($visible)) {
            $collection->visible($visible);
        }

        // 隐藏字段
        if (empty($hidden)) {
            $collection->hidden($hidden);
        }

        return $collection->toArray(false, true);
    }

    /**
     * @param $id
     * @param array $append
     * @param array $visible
     * @param array $hidden
     * @return array
     * @throws \EasySwoole\Mysqli\Exception\Exception
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \Throwable
     */
    public function getInfoById($id, $append = [], $visible=[], $hidden = [])
    {
        if (is_array($id)) {
            return $this->getOne($id, $append, $visible, $hidden);
        } else {
            return $this->getOne([$id], $append, $visible, $hidden);
        }
    }

    /**
     * 简单单个模型的添加或更新
     * @param array $data
     * @return bool|int
     * @throws \EasySwoole\Mysqli\Exception\Exception
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \Throwable
     */
    public function edit(array $data = [])
    {
        // 基础参数设置
        $id = isset($data[$this->schemaInfo()->getPkFiledName()]) ? (int)$data[$this->schemaInfo()->getPkFiledName()] : 0;

        if ($id) {
            //修改数据
            if ($this->where($this->schemaInfo()->getPkFiledName(), $id)->update($data)) {
                return $id; // 更新成功，则返回记录id
            } else {
                throw new \EasySwoole\ORM\Exception\Exception('更新失败', EXCEPTION_CODE_UPDATE);
            }
        } else {
            // 新增数据
            $result = $this->create($data)->save(); // 插入成功返回记录id
            if (is_numeric($result) || is_bool($result) && $result) {
                // 新增ID
                return $result; // 新增成功，则返回记录id
            } else {
                throw new \EasySwoole\ORM\Exception\Exception('新增失败', EXCEPTION_CODE_CREATE);
            }
        }
    }

    /**
     * 软删除记录，包括单个或多个删除
     * @param  $ids
     * @return bool
     * @throws \EasySwoole\Mysqli\Exception\Exception
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \Throwable
     */
    public function softDestroy($ids)
    {
        if ($this->where($ids)->update(['mark' => 0])) {
            return true;
        } else {
            throw new \EasySwoole\ORM\Exception\Exception('软删除失败', EXCEPTION_CODE_DELETE);
        }
    }

    /**
     * 删除数据表
     * @param $table
     * @return mixed
     * @throws \Throwable
     */
    public function dropTable($table)
    {
        if (strpos($table, DB_PREFIX) === false) {
            $table = DB_PREFIX . $table;
        }

        return $this->func(function (QueryBuilder $queryBuilder) use ($table) {
            $queryBuilder->raw("DROP TABLE {$table}");
            return true;
        });
    }

    /**
     * 绕过模型get和all方法来执行sql的方式
     * @param callable $callable
     * @return false|mixed
     * @throws \Throwable
     */
    public function customQuery(callable $callable)
    {
        return $this->func(function (QueryBuilder $queryBuilder) use ($callable) {
            return call_user_func($callable, $queryBuilder);
        });
    }

    /**
     * 启动事务
     * @return bool
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \EasySwoole\Pool\Exception\PoolEmpty
     * @throws \Throwable
     */
    public function startTrans()
    {
        // 启动事务
        return DbManager::getInstance()->startTransaction();
    }

    /**
     * 回滚事务
     * @return bool
     * @throws \Throwable
     */
    public function rollback()
    {
        // 回滚事务
        return DbManager::getInstance()->rollback();
    }

    /**
     * 提交事务
     * @return bool
     * @throws \Throwable
     */
    public function commit()
    {
        // 提交事务
        return DbManager::getInstance()->commit();
    }

    /**
     * 返回指定的请求参数
     */
    protected function getRequestParam($param = "")
    {
        /* @var Request $request */
        $request = ContextManager::getInstance()->get('request');
        if (empty($request)) {
            throw new Exception('request对象为空');
        }
        if (empty($param)) {
            return $request->getRequestParam();
        }
        return $request->getRequestParam($param);
    }

    /**
     * 定义事务快捷方法
     * @param callable $callable
     * @return mixed
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \EasySwoole\Pool\Exception\PoolEmpty
     * @throws \Throwable
     */
    public function transaction(callable $callable)
    {
        try {
            DbManager::getInstance()->startTransaction();
            $result = $callable();
            DbManager::getInstance()->commit();
            return $result;
        } catch (\Throwable $throwable) {
            DbManager::getInstance()->rollback();
            $this->exception_error($throwable);
            throw $throwable;
        }
    }

    /**
     * 获取全表所有记录的ID和Name字段
     * @return array
     * @throws \EasySwoole\ORM\Exception\Exception
     * @throws \Throwable
     */
    public function getIdName(): array
    {
        return $this->field(['id', 'name'])->all()->toArray();
    }


    /**
     * 检查表是否存在
     *
     * @param string $table 数据表名称
     * @return bool 返回结果
     */
    public function tableExists($table)
    {
        $tables = $this->getTablesList();
        if (strpos($table, DB_PREFIX) === false) {
            $table = DB_PREFIX . $table;
        }
        return in_array($table, $tables);
    }

    /**
     * 获取全部数据表
     *
     * @return array 返回结果
     */
    public function getTablesList()
    {
        $tables = [];

        $database = Config::getInstance()->getConf('MYSQL.database');

        $data = $this->func(function (QueryBuilder $queryBuilder) {
            $queryBuilder->raw('SHOW TABLES');
            return true;
        });

        foreach ($data as $v) {
            $tables[] = $v["Tables_in_{$database}"];
        }
        return $tables;
    }

    //--------------------解决通用字段问题----------------------------//
    // 覆盖all方法
    public function all($where = null)
    {
        $admin = ContextManager::getInstance()->get('user');
        $this->where($this->schemaInfo()->getTableName().'.school_code', $admin['school_code']); // $where可以覆盖这里的默认值

        $this->where($this->schemaInfo()->getTableName().'.mark', 1); // $where可以覆盖这里的默认值

        $condition = [];
        $primaryKey = $this->schemaInfo()->getPkFiledName();
        if (is_int($where)) {
            $condition = [$primaryKey => $where];
        } elseif (is_string($where)) {
            $whereKeys = explode(',', $where);
            $condition = [$primaryKey => [$whereKeys, 'IN']];
        } elseif (is_array($where)) {
            // 如果不相等说明是一个键值数组 需要批量操作where
            if (array_keys($where) !== range(0, count($where) - 1)) {
                $condition = $where;
            } else {  // 否则是一个索引数组 表示查询主键
                $condition = [$primaryKey => [$where, 'IN']];
            }
        }

        $this->where($condition);

        return parent::all(function (QueryBuilder $builder) {
            $builder = new QueryBuilder();
            $builder->setPrefix(DB_PREFIX);
        });
    }

    // 覆盖get方法
    public function get($where = null)
    {
        $admin = ContextManager::getInstance()->get('user');
        $this->where($this->schemaInfo()->getTableName().'.school_code', $admin['school_code']);
        $this->where($this->schemaInfo()->getTableName().'.mark', 1); // 查询有效记录

        $condition = [];
        $primaryKey = $this->schemaInfo()->getPkFiledName();
        if (is_int($where)) {
            $condition = [$primaryKey => $where];
        } elseif (is_string($where)) {
            $whereKeys = explode(',', $where);
            $condition = [$primaryKey => [$whereKeys, 'IN']];
        } elseif (is_array($where)) {
            // 如果不相等说明是一个键值数组 需要批量操作where
            if (array_keys($where) !== range(0, count($where) - 1)) {
                $condition = $where;
            } else {  // 否则是一个索引数组 表示查询主键
                $condition = [$primaryKey => [$where, 'IN']];
            }
        }
        $this->where($condition);

        return parent::get(function (QueryBuilder $builder) {
            $builder = new QueryBuilder();
            $builder->setPrefix(DB_PREFIX);
        });
    }

    // 模型插入都会调用的方法
    protected static function onBeforeInsert(AbstractModel $model)
    {
        $admin = ContextManager::getInstance()->get('user');
        $data = $model->getOriginData();

        // 自动增加添加人和时间
        if (empty($data['create_id'])) {
            $data['create_id'] = $admin['id'] ?: 0;
            $data['create_time'] = time();
        }

        $originData = array_merge($data, [
            'school_code' => $admin['school_code'] ?: '',
            'mark' => 1 // 记录有效
        ]);

        $model->data($originData);
        return true;
    }

    // 模型更新会调用的方法
    protected static function onBeforeUpdate(AbstractModel $model)
    {
        $admin = ContextManager::getInstance()->get('user');
        $model->where($model->schemaInfo()->getTableName().'.school_code', $admin['school_code'] ?: '');
        $model->where($model->schemaInfo()->getTableName().'.mark', 1); // 记录有效

        $data = $model->getOriginData();
        // 自动添加更新人和时间
        if (empty($data['update_id'])) {
            $originData = array_merge($data, [
                'update_id' => $admin['id'] ?? 0,
                'update_time' => time()
            ]);
            $model->data($originData);
        }

        return true;
    }

    // 模型删除会调用的方法
    protected static function onBeforeDelete(AbstractModel $model)
    {
        $admin = ContextManager::getInstance()->get('user');
        $model->where($model->schemaInfo()->getTableName().'.school_code', $admin['school_code'] ?: '');
        $model->where($model->schemaInfo()->getTableName().'.mark', 1); // 记录有效
        return true;
    }

    //--------------------字段值替换-------------------------//
    protected function getUpdateNameAttr($value, $data)
    {
        if ($this->schemaInfo()->getTableName() !== DB_PREFIX . 'admin') {
            $AdminModel = new AdminModel(); // TODO 可以优化为缓存中获取
            $userAll = $AdminModel->all()->toArray(); // 会缓存
            return $userAll[$data['update_id']]['realname'];
        } else {
            return $value;
        }
    }

    protected function getCreateNameAttr($value, $data)
    {
        if ($this->schemaInfo()->getTableName() !== DB_PREFIX . 'admin') {
            $AdminModel = new AdminModel(); // TODO 可以优化为缓存中获取
            $userAll = $AdminModel->all()->toArray(); // 会缓存
            return $userAll[$data['create_id']]['realname'];
        } else {
            return $value;
        }
    }

    protected function getUpdateTimeAttr($value, $data)
    {
        return date('Y-m-d H:i:s', $value);
    }

    protected function getCreateTimeAttr($value, $data)
    {
        return date('Y-m-d H:i:s', $value);
    }
}
